﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Runtime.Caching;
using Mefisto.Core.Caching.Contracts;

namespace Mefisto.Modules.Default.Caching
{
    [Export(typeof(ICacheProvider))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class DefaultCacheProvider : ICacheProvider
    {
        #region Constructor

        protected DefaultCacheProvider()
        {
            _cache = MemoryCache.Default;
        }

        #endregion

        #region Constants

        internal const string cacheRegionSeparator = "@";

        internal const string CacheKeyPartsSeparator = "#";

        #endregion

        #region Fields

        private int _defaultCacheDuration = 30;
        private MemoryCache _cache;
        private readonly object _lock = new object();

        #endregion

        #region Methods

        public bool Get<TValue>(out TValue value, string cacheRegion, params string[] cacheKeyParts)
        {
            lock (_lock)
            {
                var key = GetKey(cacheRegion, cacheKeyParts);

                try
                {
                    if (_cache[key] == null)
                    {
                        value = default(TValue);
                        return false;
                    }

                    value = (TValue)_cache[key];
                }
                catch
                {
                    value = default(TValue);
                    return false;
                }

                return true;
            }
        }

        public IEnumerable<KeyValuePair<string, object>> GetAll(string cacheRegion = "")
        {
            lock (_lock)
            {
                foreach (var item in _cache)
                {
                    if (string.IsNullOrWhiteSpace(cacheRegion) || GetcacheRegion(item.Key) == cacheRegion)
                        yield return new KeyValuePair<string, object>(item.Key, item.Value);
                }
            }
        }

        public void Set<TValue>(TValue value, string cacheRegion = "", params string[] cacheKeyParts)
        {
            lock (_lock)
            {
                Set<TValue>(value, _defaultCacheDuration, cacheRegion, cacheKeyParts);
            }
        }

        public void Set<TValue>(TValue value, int duration, string cacheRegion = "", params string[] cacheKeyParts)
        {
            lock (_lock)
            {
                var key = GetKey(cacheRegion, cacheKeyParts);

                var policy = new CacheItemPolicy { SlidingExpiration = new TimeSpan(0, duration, 0) };
                _cache.Set(key, value, policy);
            }
        }

        public void Remove(string cacheRegion = "", params string[] cacheKeyParts)
        {
            lock (_lock)
            {
                var key = GetKey(cacheRegion, cacheKeyParts);

                _cache.Remove(key);
            }
        }

        public void Clear(string cacheRegion = "")
        {
            lock (_lock)
            {
                var cacheKeys = new List<string>();

                foreach (var item in _cache)
                {
                    if (string.IsNullOrWhiteSpace(cacheRegion) || GetcacheRegion(item.Key) == cacheRegion) cacheKeys.Add(item.Key);
                }

                foreach (var key in cacheKeys)
                {
                    _cache.Remove(key);
                }
            }
        }

        #endregion

        #region Internal Methods

        protected string GetKey(string cacheRegion, string[] cacheKeyParts)
        {
            return string.Format("{0}{1}{2}", cacheRegion, cacheRegionSeparator, string.Join(CacheKeyPartsSeparator, cacheKeyParts));
        }

        protected string GetcacheRegion(object key)
        {
            try
            {
                return key.ToString().Split(new[] { cacheRegionSeparator }, StringSplitOptions.RemoveEmptyEntries)[0];
            }
            catch (Exception)
            {
                return "";
            }
        }

        protected string[] GetCacheKeyParts(string key)
        {
            try
            {
                return key.Split(new[] { cacheRegionSeparator }, StringSplitOptions.RemoveEmptyEntries)[1].Split(new[] { CacheKeyPartsSeparator }, StringSplitOptions.RemoveEmptyEntries);
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion
    }
}